<!DOCTYPE html>
<title>CSS Nesting: CSSNestedDeclarations CSSOM</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      .a {
        & { --x:1; }
        --x:2;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    assert_equals(outer.cssRules.length, 2);
    assert_equals(outer.cssRules[0].cssText, `& { --x: 1; }`);
    assert_equals(outer.cssRules[1].cssText, `--x: 2;`);
  }, 'Trailing declarations');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      .a {
        --a:1;
        --b:1;
        & { --c:1; }
        --d:1;
        --e:1;
        & { --f:1; }
        --g:1;
        --h:1;
        --i:1;
        & { --j:1; }
        --k:1;
        --l:1;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    assert_equals(outer.cssRules.length, 6);
    assert_equals(outer.cssRules[0].cssText, `& { --c: 1; }`);
    assert_equals(outer.cssRules[1].cssText, `--d: 1; --e: 1;`);
    assert_equals(outer.cssRules[2].cssText, `& { --f: 1; }`);
    assert_equals(outer.cssRules[3].cssText, `--g: 1; --h: 1; --i: 1;`);
    assert_equals(outer.cssRules[4].cssText, `& { --j: 1; }`);
    assert_equals(outer.cssRules[5].cssText, `--k: 1; --l: 1;`);
  }, 'Mixed declarations');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      .a {
        & { --x:1; }
        --y:2;
        --z:3;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    assert_equals(outer.cssRules.length, 2);
    let nested_declarations = outer.cssRules[1];
    assert_true(nested_declarations instanceof CSSNestedDeclarations);
    assert_equals(nested_declarations.style.length, 2);
    assert_equals(nested_declarations.style.getPropertyValue('--x'), '');
    assert_equals(nested_declarations.style.getPropertyValue('--y'), '2');
    assert_equals(nested_declarations.style.getPropertyValue('--z'), '3');
  }, 'CSSNestedDeclarations.style');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      .a {
        @media (width > 100px) {
          --x:1;
          --y:1;
          .b { }
          --z:1;
        }
        --w:1;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    assert_equals(outer.cssRules.length, 2);

    // @media
    let media = outer.cssRules[0];
    assert_equals(media.cssRules.length, 3);
    assert_true(media.cssRules[0] instanceof CSSNestedDeclarations);
    assert_equals(media.cssRules[0].cssText, `--x: 1; --y: 1;`);
    assert_equals(media.cssRules[1].cssText, `& .b { }`);
    assert_true(media.cssRules[2] instanceof CSSNestedDeclarations);
    assert_equals(media.cssRules[2].cssText, `--z: 1;`);

    assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
    assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
  }, 'Nested group rule');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      .a {
        @scope (.foo) {
          --x:1;
          --y:1;
          .b { }
          --z:1;
        }
        --w:1;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    if (window.CSSScopeRule) {
      assert_equals(outer.cssRules.length, 2);

      // @scope
      let scope = outer.cssRules[0];
      assert_true(scope instanceof CSSScopeRule);
      assert_equals(scope.cssRules.length, 3);
      assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
      assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
      assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
      assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
      assert_equals(scope.cssRules[2].cssText, `--z: 1;`);

      assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
      assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
    } else {
      assert_equals(outer.cssRules.length, 0);
    }
  }, 'Nested @scope rule');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync(`
      a {
        & { --x:1; }
        width: 100px;
        height: 200px;
        color:hover {}
        --y: 2;
      }
    `);
    assert_equals(s.cssRules.length, 1);
    let outer = s.cssRules[0];
    assert_equals(outer.cssRules.length, 4);
    assert_equals(outer.cssRules[0].cssText, `& { --x: 1; }`);
    assert_equals(outer.cssRules[1].cssText, `width: 100px; height: 200px;`);
    assert_equals(outer.cssRules[2].cssText, `& color:hover { }`);
    assert_equals(outer.cssRules[3].cssText, `--y: 2;`);
  }, 'Inner rule starting with an ident');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync('.a {}');
    assert_equals(s.cssRules.length, 1);
    let a_rule = s.cssRules[0];
    assert_equals(a_rule.cssRules.length, 0);
    a_rule.insertRule(`
      width: 100px;
      height: 200px;
    `);
    assert_equals(a_rule.cssRules.length, 1);
    assert_true(a_rule.cssRules[0] instanceof CSSNestedDeclarations);
    assert_equals(a_rule.cssRules[0].cssText, `width: 100px; height: 200px;`);
  }, 'Inserting a CSSNestedDeclaration rule into style rule');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync('.a { @media (width > 100px) {} }');
    assert_equals(s.cssRules.length, 1);
    assert_equals(s.cssRules[0].cssRules.length, 1);
    let media_rule = s.cssRules[0].cssRules[0];
    assert_true(media_rule instanceof CSSMediaRule);
    assert_equals(media_rule.cssRules.length, 0);
    media_rule.insertRule(`
      width: 100px;
      height: 200px;
    `);
    assert_equals(media_rule.cssRules.length, 1);
    assert_true(media_rule.cssRules[0] instanceof CSSNestedDeclarations);
    assert_equals(media_rule.cssRules[0].cssText, `width: 100px; height: 200px;`);
  }, 'Inserting a CSSNestedDeclaration rule into nested group rule');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync('@media (width > 100px) {}');
    assert_equals(s.cssRules.length, 1);
    let media_rule = s.cssRules[0];
    assert_true(media_rule instanceof CSSMediaRule);
    assert_equals(media_rule.cssRules.length, 0);
    assert_throws_dom('SyntaxError', () => {
      media_rule.insertRule(`
        width: 100px;
        height: 200px;
      `);
    });
  }, 'Attempting to insert a CSSNestedDeclaration rule into top-level @media rule');

  test(() => {
    let sheet = new CSSStyleSheet();
    assert_throws_dom('SyntaxError', () => {
      sheet.insertRule(`
        width: 100px;
        height: 200px;
      `);
    });
  }, 'Attempting to insert a CSSNestedDeclaration rule into a stylesheet');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync('.a {}');
    assert_equals(s.cssRules.length, 1);
    let a_rule = s.cssRules[0];
    assert_equals(a_rule.cssRules.length, 0);
    assert_throws_dom('SyntaxError', () => {
      a_rule.insertRule('');
    });
  }, 'Attempting to insert a CSSNestedDeclaration rule, empty block');

  test(() => {
    let s = new CSSStyleSheet();
    s.replaceSync('.a {}');
    assert_equals(s.cssRules.length, 1);
    let a_rule = s.cssRules[0];
    assert_equals(a_rule.cssRules.length, 0);
    assert_throws_dom('SyntaxError', () => {
    a_rule.insertRule(`
      xwidth: 100px;
      xheight: 200px;
    `);
    });
  }, 'Attempting to insert a CSSNestedDeclaration rule, all invalid declarations');
</script>
